#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include <sys/resource.h>

#include "sys_util.h"
#include "watchworker.h"
#include "Apiapr.h"
#include "redispool.h"

int listen_sd;
myevent_s* g_Events;
WEB_SERVER_CFG server_cfg;

void EventSet(myevent_s *ev, int fd, int events)
{
	ev->fd = fd;
	ev->events = events;
	ev->last_active = time(NULL);
}

void IPSet(myevent_s *ev, const char* ip, unsigned short port)
{
	memcpy(ev->rIp, ip, 16);
	ev->rPort = port;
}

void EventAdd(int epollFd, myevent_s *ev)
{
	struct epoll_event epv = {0, {0}};
	epv.data.ptr = ev;
	epv.events = ev->events;
	int fd = ev->fd;
	if(epoll_ctl(epollFd, EPOLL_CTL_ADD, fd, &epv) < 0)
		printf("epoll add failure: fd=%d\n", fd);
//   else
//    printf("epoll add success: fd=%d\n", fd);
}

void EventMod(int epollFd, myevent_s *ev)
{
	struct epoll_event epv = {0, {0}};
	epv.data.ptr = ev;
	epv.events = ev->events;
	int fd = ev->fd;
	if(epoll_ctl(epollFd, EPOLL_CTL_MOD, fd, &epv) < 0)
		printf("epoll mod failure: fd=%d\n", fd);
//  else
//		printf("epoll mod success: fd=%d\n", fd);
}

void EventDel(int epollFd, myevent_s *ev)
{
	struct epoll_event epv = {0, {0}};
	epv.data.ptr = ev;
	int fd = ev->fd;
	if(epoll_ctl(epollFd, EPOLL_CTL_DEL, fd, &epv) < 0)
		printf("epoll del failure: fd=%d\n", fd);
//   	else
//      printf("epoll del success: fd=%d\n", fd);

	close(fd);
	FreeSessionPool(ev);
	memset(ev, 0x00, sizeof(myevent_s));
}

int InitListenSocket(const char *ip4, const int port, const int max_link)
{
	struct sockaddr_in addr4;
	bzero(&addr4, sizeof(addr4));
	inet_pton(AF_INET, ip4, &(addr4.sin_addr));
	addr4.sin_family = AF_INET;
	addr4.sin_port = htons(port);

	int listen_sd = socket(AF_INET, SOCK_STREAM, 0);
	if (0 > listen_sd) return(-1);
 
/*
    unsigned int nRecvBuf = 64*1024;
    if(setsockopt(listen_sd, SOL_SOCKET, SO_RCVBUF, &nRecvBuf, 4)<0)
    	printf("%s: setsockopt SO_RCVBUF failure:%s\n", __func__, strerror(errno));
*/

	unsigned int optval = 0x1;
	if(setsockopt(listen_sd, SOL_SOCKET, SO_REUSEADDR, &optval, 4)<0)
		printf("%s: setsockopt SO_REUSEADDR failure:%s\n", __func__, strerror(errno));

	struct linger	optval1;
	optval1.l_onoff = 1;
	optval1.l_linger = 60;
	if(setsockopt(listen_sd, SOL_SOCKET, SO_LINGER, &optval1, sizeof(struct linger))<0)
		printf("%s: setsockopt SO_LINGER failure:%s\n", __func__, strerror(errno));

	int	flags;
	flags = fcntl(listen_sd, F_GETFL, 0);
	fcntl(listen_sd, F_SETFL, flags | O_NONBLOCK);

	if (0 > bind(listen_sd, (struct sockaddr *)&addr4, sizeof(addr4)))
	{
		printf("bind socket failure:%s\n", strerror(errno));
		close(listen_sd);
		return(-1);
	}

	if (0 > listen(listen_sd, max_link))
	{
		printf("listen socket failure:%s\n", strerror(errno));
		close(listen_sd);
		return(-1);
	}
  
	return(listen_sd);
}

void AcceptConn(int epoll_fd, int fd)
{
	struct sockaddr_in sin;
	socklen_t len = sizeof(struct sockaddr_in);
	int nfd, loops;

	for (loops = 0; loops < 100; loops++)
	{
		if((nfd = accept(fd, (struct sockaddr*)&sin, &len)) == -1)
		{
			if(errno != EAGAIN && errno != EINTR)
			{
				printf("ACCEPT: process[%d]: bad accept\n", getpid());
			}
			return;
		}
		do
		{
			if(nfd == server_cfg.EPOLL_SD_SIZE)
			{
				printf("ACCEPT: process[%d]: max connection limit[%d]\n", getpid(), server_cfg.EPOLL_SD_SIZE);
				break;
			}

			if(fcntl(nfd, F_SETFL, O_NONBLOCK) < 0)
				break;

			printf("ACCEPT: process[%d]: accept new connection %s:%u\n", getpid(), inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));

			EventSet(g_Events+nfd, nfd, EPOLLIN|EPOLLET|EPOLLHUP);
			IPSet(g_Events+nfd, inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
			InitSessionPool(g_Events+nfd, server_cfg.APR_SESSION_MAXSIZE);

			EventAdd(epoll_fd, g_Events+nfd);
		}while(0);
	}
}

void RecvData(int epoll_fd, struct epoll_event* events)
{
	myevent_s * ev = events->data.ptr;

	int fd = -1;
	if ((fd = ev->fd) < 0)
		return;

	InitRequireFrame(ev, server_cfg.APR_REQUIRE_MAXSIZE);

	int ret = RecvApi_nonb(fd, &(ev->frame));

	if(ret == 0) //success
	{
		printf("RECV: OK! pid[%d]: fd[%d]: from [%s:%u]: %s\n", getpid(), fd, ev->rIp, ev->rPort, ev->frame.pBuf);
		PushRedisList(ev->frame.pBuf);
		EventSet(ev, fd, EPOLLOUT|EPOLLET|EPOLLHUP);
		EventMod(epoll_fd, ev);
	}
	else if(ret == 1) //incomplete
	{
		printf("RECV: OK but need a lot! pid[%d]: fd[%d]: from [%s:%u]: %s\n", getpid(), fd, ev->rIp, ev->rPort, ev->frame.pBuf);
	}
	else if(ret == -1) //error
	{
		printf("RECV: FAILURE! pid[%d]: fd[%d]: from [%s:%u] error[%d]: %s\n", getpid(), fd, ev->rIp, ev->rPort, errno, strerror(errno));  
		EventSet(ev, fd, 0);
		EventDel(epoll_fd, ev);
	}
	else if(ret == -2) //close
	{
		printf("RECV: CLOSE! pid[%d]: fd[%d]: from [%s:%u]: closed gracefully\n", getpid(), fd, ev->rIp, ev->rPort);  
		EventSet(ev, fd, 0);
		EventDel(epoll_fd, ev);
	}
	else if(ret == -3) //overflow
	{
		printf("RECV: OVERFLOW! pid[%d]: fd[%d]: from [%s:%u]: need more memory\n", getpid(), fd, ev->rIp, ev->rPort);  
		EventSet(ev, fd, 0);
		EventDel(epoll_fd, ev);
	}
}

// send data  
void SendData(int epoll_fd, struct epoll_event* events)
{
	myevent_s * ev = events->data.ptr;

	int fd = -1;
	if ( (fd = ev->fd) < 0)
		return;

	int i =0;
	while(i<3)
	{
		if(PopRedisList(ev->frame.pBuf) == 0){
			break;
		}else{
			sleep(0.1);
		}
		i++;
		
		if(i==3){
			strcpy(ev->frame.pBuf, "redis timeout and not find result\n");
		}
	}
	
	int ret = SendApi_nonb(fd, &(ev->frame));
	if(ret == 0) //success
	{
		printf("SEND: OK! pid[%d]: fd[%d]: to [%s:%u]: %s\n", getpid(), fd, ev->rIp, ev->rPort, ev->frame.pBuf);
		FreeRequireFrame(ev); //require finish 
		EventSet(ev, fd, EPOLLIN|EPOLLET|EPOLLHUP);
		EventMod(epoll_fd, ev);
	}
	else if(ret == 1) //success but need more data 
	{
		printf("SEND: OK but need a lot! pid[%d]: fd[%d]: to [%s:%u]: %s\n", getpid(), fd, ev->rIp, ev->rPort, ev->frame.pBuf);
	}
	else if(ret == -1) //error
	{
		printf("SEND: FAILURE! pid[%d]: fd[%d]: to [%s:%u]: error[%d]: %s\n", getpid(), fd, ev->rIp, ev->rPort, errno, strerror(errno));
		EventSet(ev, fd, 0);
		EventDel(epoll_fd, ev);	
	}
	else if(ret == -2) //connection close
	{
		printf("SEND: CLOSE! pid[%d]: fd[%d]: to [%s:%u]: closed gracefully\n", getpid(), fd, ev->rIp, ev->rPort);
		EventSet(ev, fd, 0);
		EventDel(epoll_fd, ev);	
	}
}

void InitResource()
{
	struct rlimit rlNew, rlOld;

	getrlimit(RLIMIT_NOFILE, &rlOld);
//	printf("soft:%d, must:%d \n", rlOld.rlim_cur, rlOld.rlim_max);
	
	rlNew.rlim_cur = 1024*10;
	rlNew.rlim_max = rlOld.rlim_max;
	
	setrlimit(RLIMIT_NOFILE, &rlNew);
	printf("modify soft fd num from %d to %d MAX[%d]\n", rlOld.rlim_cur, rlNew.rlim_cur, rlNew.rlim_max);
}

int main(int argc, char **argv)
{
	InitParam(argc, argv);

	InitResource();

	InitAprPool();
	
	InitRedisPool();

	int new_fd, kdpfd, nfds, n, ret, curfds;
	socklen_t len;
	struct sockaddr_in my_addr, their_addr;

	struct epoll_event events[server_cfg.EPOLL_SD_SIZE];

	g_Events = (myevent_s*)malloc(sizeof(myevent_s)*(server_cfg.EPOLL_SD_SIZE));
	memset(g_Events, 0x00, sizeof(myevent_s)*(server_cfg.EPOLL_SD_SIZE));

	listen_sd = InitListenSocket(server_cfg.WEB_SERVER_HOST, server_cfg.WEB_SERVER_PORT, 5);

	if(listen_sd < 0)
	{
		printf("initial listen socket error\n");
		return;
	}

	InitProcess(server_cfg.EPOLL_FORK_NUM);

	int epll_fd = epoll_create(server_cfg.EPOLL_SD_SIZE);
	len = sizeof(struct sockaddr_in);

	EventSet(g_Events+0, listen_sd, EPOLLIN|EPOLLET|EPOLLHUP);
	EventAdd(epll_fd, g_Events+0);

	while (1) 
	{
		nfds = epoll_wait(epll_fd, events, server_cfg.EPOLL_SD_SIZE, -1);
		if (nfds == -1) 
		{
			perror("epoll_wait");
			break;
		}

		for (n = 0; n < nfds; ++n) 
		{
			myevent_s* ev = events[n].data.ptr;

			if (ev->fd == listen_sd) 
			{
				AcceptConn(epll_fd, listen_sd);
			}
			else if(events[n].events & EPOLLIN)
			{
				RecvData(epll_fd, events+n);
			}
			else if(events[n].events & EPOLLOUT)
			{
				SendData(epll_fd, events+n);
			}
			else 
			{
				myevent_s * ev = events[n].data.ptr;
//				EventSet(ev, ev->fd, 0);
				EventDel(epll_fd, ev);
			}
		}
		
		for(n = 0; n < server_cfg.EPOLL_SD_SIZE; ++n)
		{
			if(g_Events[n].last_active-time(NULL)>30)
			{
				EventDel(epll_fd, g_Events+n);
			}
		}
		
		CheckRedisPool();
	}

	close(listen_sd); 
	return 0;
}
